package com.atguigu.tingshu.common.login;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import jakarta.servlet.http.HttpServletRequest;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;

/***
 * 自定义切面类：用于登录校验
 */
@Component
@Aspect
public class LoginAspect {

    /**
     * 增强方法: 登录校验
     * 在方法执行之前检查用户是否已经登录
     *
     * @param point 切入点
     * @return 方法执行结果
     */
    @SneakyThrows
    @Around("@annotation(com.atguigu.tingshu.common.login.GuiguLogin)")
    public Object loginCheck(ProceedingJoinPoint point) {

        // 获取当前执行的方法对象的签名
        MethodSignature signature = (MethodSignature) point.getSignature();
        // 获取方法对象
        Method method = signature.getMethod();
        // 获取上面的注解
        GuiguLogin guiguLogin = method.getAnnotation(GuiguLogin.class);


        // 检查@GuiguLogin注解中的isLogin()返回值是否为true
        if (guiguLogin.isLogin()) {

            // 获取当前请求对象 用于检查用户是否携带了登录令牌
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = requestAttributes.getRequest();
            // 从request中获取用户token: 硬性要求放在请求头
            String token = request.getHeader("token");

            // 没有携带 让用户去登录
            if (StringUtils.isEmpty(token)) {
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }

            // 解析jwt
            try {
                Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(SystemConstant.PUBLIC_KEY));
                // 获取载荷
                String claims = jwt.getClaims();
                // 反序列化
                JSONObject jsonObject = JSONObject.parseObject(claims, JSONObject.class);


                // 获取用户的登录时间
                Long time = Long.valueOf(jsonObject.get("time").toString());
                // 判断是否超时
                if (System.currentTimeMillis() - time > 300000) {
                    // 令牌超时,去登录
                    throw new GuiguException(ResultCodeEnum.SIGN_OVERDUE);
                }

                // 获取用户id
                Long userId = Long.valueOf(jsonObject.get("userId").toString());

                // 获取用户的vip过期时间
                Long vipExpireTime = Long.valueOf(jsonObject.get("vipExpireTime").toString());
                if (vipExpireTime > System.currentTimeMillis()) {
                    // vip过期
                    AuthContextHolder.setVip(1);
                } else {
                    // vip未过期
                    AuthContextHolder.setVip(0);
                }

                // 验证通过 将token中的用户ID设置到当前线程上下文 以便后续操作使用
                AuthContextHolder.setUserId(userId);

            } catch (Exception e) {
                // 解析令牌不合法 去登录
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }
        }
        // 获取方法参数
        Object[] args = point.getArgs();
        // 执行被拦截的方法
        Object proceed = point.proceed(args);
        // 清除本地线程类
        AuthContextHolder.removeUserId();
        // 返回结果
        return proceed;
    }
}
